#ifndef SHADER_OBJECT_H
#define SHADER_OBJECT_H

#include "GlobalDefines.hpp"
#include "Graphics.hpp"
#include "VertexAttributes.hpp"

#include <vector>
#include <fstream>

#define INACTIVE_UNIFORM -1

class ShaderObject
{
public:
  ShaderObject();
  ShaderObject(std::string filename);

  ~ShaderObject();

  void Load(std::string filename, bool loadGeometry=false);
  void Enable();
  void Disable();
  void Reload();

  void SetUniform1i(std::string name, int i);
  void SetUniform1f(std::string name, float f);
  void SetUniformVec3f(std::string name, Vec3f v);
  void SetUniformVec3d(std::string name, Vec3d v);
  void SetUniformVec3i(std::string name, Vec3i v);

  void SetUniformMatrix4f(std::string name, const GLfloat *value);

  void SetSampler2D(std::string name, GLuint unit, GLuint texture);
  void SetSampler3D(std::string name, GLuint unit, GLuint texture);
  void SetSampler2DArray(std::string name, GLuint unit, GLuint texture);

  // Temporary
  GLuint GetProgram();
  GLuint GetProgramId();

  static void InitDefaultShaders();

  static ShaderObject* GetBaseShader();
  static ShaderObject* GetBaseColorShader();

  static ShaderObject* baseShader;
  static ShaderObject* baseColorShader;
    
private:
  GLuint m_program;
  GLuint m_vertexShader;
  GLuint m_geometryShader;
  GLuint m_fragmentShader;

  std::string programName;

  static GLuint enabled;

  std::map<std::string, GLint> uniformLocations;
  GLint GetUniformLocation(std::string uniform);

  void BindAttributeLocations();

  std::string ReadShaderFile(std::string filename);
  GLuint LoadFromMemory(const char *source, GLenum type);
  GLuint LoadFromFile(std::string filename, GLenum type);
  void ShowInfoLog(GLuint object, PFNGLGETSHADERIVPROC glGet__iv, PFNGLGETSHADERINFOLOGPROC glGet__InfoLog);
};

#endif